package handler

import (
	"context"
	"crypto/sha512"
	"errors"
	"fmt"
	"github.com/anaskhan96/go-password-encoder"
	"github.com/golang/protobuf/ptypes/empty"
	uuid "github.com/satori/go.uuid"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
	"gorm.io/gorm"
	"project05/src/global"
	"project05/src/model"
	"project05/src/proto"
	"strings"
)

type UserServer struct {
}

//type UserServiceServer interface {
//	GetUserList(context.Context, *PageInfo) (*UserListResponse, error)
//	GetUserByUserName(context.Context, *UserNameRequest) (*UserInfoResponse, error)
//	GetUserByUserId(context.Context, *IdRequest) (*UserInfoResponse, error)
//	GetUserByPhone(context.Context, *PhoneRequest) (*UserInfoResponse, error)
//	CreateUser(context.Context, *CreateUserInfo) (*UserInfoResponse, error)
//	UpdateUser(context.Context, *UpdateUserInfo) (*emptypb.Empty, error)
//	UpdateUserPassword(context.Context, *UpdatePasswordRequest) (*emptypb.Empty, error)
//	UpdateUserRoleId(context.Context, *UpdateRoleIdRequest) (*emptypb.Empty, error)
//	CheckPassword(context.Context, *CheckPasswordInfo) (*CheckResponse, error)
//	DeleteUser(context.Context, *DeleteUserRequest) (*emptypb.Empty, error)
//}

func Paginate(page, pageSize int) func(db *gorm.DB) *gorm.DB {
	return func(db *gorm.DB) *gorm.DB {

		if page == 0 {
			page = 1
		}

		switch {
		case pageSize > 100:
			pageSize = 100
		case pageSize <= 0:
			pageSize = 10
		}
		offset := (page - 1) * pageSize
		return db.Offset(offset).Limit(pageSize)
	}
}

func UserModel2UserInfoResponse(user model.User) proto.UserInfoResponse {
	return proto.UserInfoResponse{
		Id:       int32(user.ID),
		UserName: user.UserName,
		Password: user.Password,
		Phone:    user.Phone,
		Name:     user.Name,
		Identify: user.Identify,
		RoleID:   int32(user.RoleID),
		Ip:       user.Ip,
	}
}

func (s *UserServer) DeleteUser(ctx context.Context, req *proto.DeleteUserRequest) (*emptypb.Empty, error) {
	var userModel model.User
	result := global.Db.Where("user_id = ?", req.UserId).First(&userModel)
	if errors.Is(result.Error, gorm.ErrRecordNotFound) {
		return nil, status.Errorf(codes.AlreadyExists, "用户不存在")
	}

	res := global.Db.Where("user_id = ?", req.UserId).Delete(&userModel)
	if res.Error != nil {
		return nil, status.Errorf(codes.Internal, "内部错误")
	}

	return &empty.Empty{}, nil
}

func (s *UserServer) UpdateUserRoleId(ctx context.Context, req *proto.UpdateRoleIdRequest) (*emptypb.Empty, error) {
	var userModel model.User
	result := global.Db.Where("id = ?", req.UserId).First(&userModel)
	if errors.Is(result.Error, gorm.ErrRecordNotFound) {
		return nil, status.Errorf(codes.AlreadyExists, "用户不存在")
	}

	userModel.RoleID = int(req.RoleId)
	res := global.Db.Model(&userModel).Where("id = ?", req.UserId).Updates(userModel)
	if res.Error != nil {
		return nil, status.Errorf(codes.Internal, "内部错误")
	}

	return &empty.Empty{}, nil
}

func (s *UserServer) UpdateUserPassword(ctx context.Context, req *proto.UpdatePasswordRequest) (*emptypb.Empty, error) {
	var userModel model.User
	result := global.Db.Where("id = ?", req.Id).First(&userModel)
	if errors.Is(result.Error, gorm.ErrRecordNotFound) {
		return nil, status.Errorf(codes.AlreadyExists, "用户不存在")
	}

	//加密新密码
	option := password.Options{
		SaltLen:      16,
		Iterations:   100,
		KeyLen:       32,
		HashFunction: sha512.New,
	}
	salt, encodePassword := password.Encode(req.Password, &option)
	newPassword := fmt.Sprintf("$pbkdf2-sha512$%s$%s", salt, encodePassword)
	userModel.Password = newPassword

	res := global.Db.Model(&userModel).Where("id = ?", req.Id).Updates(&userModel)
	if res.Error != nil {
		return nil, status.Errorf(codes.Internal, "内部错误")
	}

	return &empty.Empty{}, nil
}

func (s *UserServer) GetUserList(ctx context.Context, req *proto.PageInfo) (*proto.UserListResponse, error) {
	var users []model.User
	result := global.Db.Scopes(Paginate(int(req.Pn), int(req.PSize))).Find(&users)
	if result.Error == gorm.ErrRecordNotFound {
		return nil, status.Errorf(codes.Internal, "没有找到数据")
	}
	if result.Error != nil {
		return nil, result.Error
	}

	rsp := &proto.UserListResponse{}
	rsp.Total = int32(result.RowsAffected)

	for _, user := range users {
		userResp := UserModel2UserInfoResponse(user)
		rsp.Date = append(rsp.Date, &userResp)
	}

	return rsp, nil
}

func (s *UserServer) GetUserByUserId(ctx context.Context, req *proto.IdRequest) (*proto.UserInfoResponse, error) {
	var user model.User
	res := global.Db.Where("user.id = ?", req.Id).First(&user)
	if res.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "用户不存在")
	}
	if res.Error != nil {
		return nil, res.Error
	}

	result := UserModel2UserInfoResponse(user)
	return &result, nil
}

func (s *UserServer) GetUserByUserName(ctx context.Context, req *proto.UserNameRequest) (*proto.UserInfoResponse, error) {
	var user model.User
	res := global.Db.Where("user.user_name = ?", req.UserName).First(&user)
	if res.Error != nil {
		if res.Error == gorm.ErrRecordNotFound {
			return nil, status.Errorf(codes.NotFound, "不存在该用户")
		}
		return nil, status.Errorf(codes.Internal, res.Error.Error())
	}
	if res.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "没找到该用户")
	}
	result := UserModel2UserInfoResponse(user)
	return &result, nil
}

func (s *UserServer) CreateUser(ctx context.Context, req *proto.CreateUserInfo) (*proto.UserInfoResponse, error) {
	//新建用户前需要查询其是否存在
	var user model.User
	res := global.Db.Where("user.identify = ?", req.Identify).First(&user)
	resByPhone := global.Db.Where("user.phone = ?", req.Phone).First(&user)

	if res.RowsAffected == 1 || resByPhone.RowsAffected == 1 {
		return nil, status.Errorf(codes.AlreadyExists, "用户已存在")
	}

	user.UserName = req.UserName
	user.Identify = req.Identify
	user.Phone = req.Phone
	user.Name = req.Name

	//密码加密
	option := password.Options{
		SaltLen:      16,
		Iterations:   100,
		KeyLen:       32,
		HashFunction: sha512.New,
	}
	salt, encodePassword := password.Encode(req.Password, &option)
	newPassword := fmt.Sprintf("$pbkdf2-sha512$%s$%s", salt, encodePassword)
	user.Password = newPassword

	result := global.Db.Create(&user)
	if result.Error != nil {
		return nil, status.Errorf(codes.Internal, result.Error.Error())
	}

	userInfoRes := UserModel2UserInfoResponse(user)
	return &userInfoRes, nil
}

func (s *UserServer) UpdateUser(ctx context.Context, req *proto.UpdateUserInfo) (*emptypb.Empty, error) {
	var user model.User
	res := global.Db.Where("user.id = ?", req.Id).First(&user)
	if res.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "用户不存在")
	}

	user.UserName = req.UserName
	user.Phone = req.Phone

	result := global.Db.Save(&user)
	if result.Error != nil {
		return nil, status.Errorf(codes.Internal, "修改失败")
	}

	return &empty.Empty{}, nil
}

func (s *UserServer) CheckPassword(ctx context.Context, req *proto.CheckPasswordInfo) (*proto.CheckResponse, error) {
	option := password.Options{
		SaltLen:      16,
		Iterations:   100,
		KeyLen:       32,
		HashFunction: sha512.New,
	}
	passwordInfo := strings.Split(req.EncryptPassword, "$")
	check := password.Verify(req.Password, passwordInfo[2], passwordInfo[3], &option)
	return &proto.CheckResponse{Success: check}, nil
}

func (s *UserServer) GetUserByPhone(ctx context.Context, req *proto.PhoneRequest) (*proto.UserInfoResponse, error) {
	var user model.User
	result := global.Db.Where("user.phone = ?", req.Phone).First(&user)

	if result.Error != nil {
		return nil, status.Errorf(codes.Internal, "内部错误")
	}
	if result.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "未找到该用户")
	}

	res := UserModel2UserInfoResponse(user)
	return &res, nil
}
func (s *UserServer) GetUserByEmail(ctx context.Context, req *proto.EmailRequest) (*proto.UserInfoResponse, error) {
	var user model.User
	result := global.Db.Where("user.email = ?", req.Email).First(&user)

	if result.Error != nil {
		return nil, status.Errorf(codes.Internal, "内部错误")
	}
	if result.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "未找到该用户")
	}
	res := UserModel2UserInfoResponse(user)
	return &res, nil
}
func (s *UserServer) CreateSmsUser(ctx context.Context, req *proto.SmsInfo) (*proto.UserInfoResponse, error) {
	var user model.User

	tx := global.Db.Begin()

	//邮箱注册
	if req.Phone == "" && req.Email != "" {
		tx.Where("user.email = ?", req.Email).Find(&user)
		if tx.RowsAffected > 0 {
			return nil, status.Errorf(codes.Internal, "邮箱已被使用")
		}
		user.Email = req.Email
		//手机注册
	} else if req.Phone != "" && req.Email == "" {
		tx.Where("user.Phone = ?", req.Phone).Find(&user)
		if tx.RowsAffected > 0 {
			return nil, status.Errorf(codes.Internal, "手机已被使用")
		}
		user.Phone = req.Phone
	} else {
		//两种都不是
		return nil, status.Errorf(codes.Internal, "非法传入")
	}

	user.UserName = "用户" + uuid.NewV4().String()
	user.RoleID = int(req.RoleId)

	//密码加密
	option := password.Options{
		SaltLen:      16,
		Iterations:   100,
		KeyLen:       32,
		HashFunction: sha512.New,
	}
	salt, encodePassword := password.Encode(req.Password, &option)
	newPassword := fmt.Sprintf("$pbkdf2-sha512$%s$%s", salt, encodePassword)
	user.Password = newPassword

	//注册角色为用户或者管理员时；不需要身份证号，个人家政人员需要；企业需要法人身份证
	if req.RoleId != 3 && req.RoleId != 1 {
		user.Identify = req.Identify
	}
	tx.Create(&user)
	if tx.Error != nil {
		tx.Rollback()
		return nil, status.Errorf(codes.Internal, "注册失败，内部错误")
	}
	tx.Commit()
	resp := UserModel2UserInfoResponse(user)
	return &resp, nil
}
func (s *UserServer) GetUserLastIp(ctx context.Context, req *proto.UserId) (*proto.UserIpResponse, error) {
	var user model.User
	result := global.Db.Where("user_id = ?", req.Id).First(&user)
	if result.Error != nil {
		return nil, status.Errorf(codes.Internal, "内部错误")
	}
	if result.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "未找到该用户")
	}
	return &proto.UserIpResponse{Ip: user.Ip}, nil
}
